// Implementation of HTTP worker pool with libcurl
// Copyright 2006 (C) Ralph Thomas

#include <model/http/httpModel.h>
#include <util/thread.h>
#include <curl/curl.h>
#include <boost/shared_ptr.hpp>
#include <model/error.h>

#include <iostream>

namespace model {
	class httpWorker : public util::thread::work {
		std::string	_url;	///< URL to fetch data from.
		httpModel*	_mod;	///< Model to inform.
		static size_t _write( void* ptr, size_t size, size_t no,
			void* data ) {
			//
			// Get the string from the data.
			//
			if( !data ) return 0;
			if( !ptr ) return 0;
			std::string* target( reinterpret_cast<std::string*>(
				data ) );
			*target += std::string( reinterpret_cast<char*>( ptr ),
				size * no );
			return size * no;
		}
	  public:
		httpWorker( const std::string& url, const httpModel* mod ) {
			_url = url;
			_mod = static_cast<httpModel*>( mod->retain() );
		}
		virtual ~httpWorker() {
			if( _mod ) _mod->release();
		}
		virtual void doWork() {
		try {
			//
			// Do curl work here.
			//
			std::string	output;
			char*		mime( 0 );
			std::string	mimeType;
			CURL* my_curl( curl_easy_init() );
			CURLcode error( CURLE_OK );
			curl_easy_setopt( my_curl, CURLOPT_NOPROGRESS, 1 );
			curl_easy_setopt( my_curl, CURLOPT_URL, _url.c_str() );
			curl_easy_setopt( my_curl, CURLOPT_WRITEFUNCTION,
				&_write );
			curl_easy_setopt( my_curl, CURLOPT_WRITEDATA, 
				reinterpret_cast<void*>( &output ) );
			curl_easy_setopt( my_curl, CURLOPT_USERAGENT,
				"MissionCode.org Software" );
			error = curl_easy_perform( my_curl );
			curl_easy_getinfo( my_curl, CURLINFO_CONTENT_TYPE,
				&mime );
			if( mime ) mimeType = mime;
			curl_easy_cleanup( my_curl );
			if( !error ) {
				//
				// Everything worked! Tell the model.
				//
				_mod->load( mimeType, output );
			} else {
				_mod->setAttribute( kError, adobe::value_t(
					6 ) );
			}
		} catch( ... ) {}
		}
		virtual priority getPriority() const {
			return kWorkPriorityRegular;
		}
	};
	static util::thread* worker = 0;
	void httpFetch( const httpModel* mod, const std::string& theUrl ) {
		if( !mod ) return;
		if( theUrl.empty() ) return;
		//
		// Ensure that CURL is initialized.
		//
		curl_global_init( CURL_GLOBAL_ALL );
		//
		// Create the thread now if it hasn't been created already.
		// This code will be replaced before too long, and a proper
		// multiple-worker pool implemented.
		//
		if( !worker ) worker = new util::thread();
		worker->adoptWork( new httpWorker( theUrl, mod ) );
	}
};
